package com.skyjing.askit.view.roundimageview

import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.drawable.Drawable
import android.graphics.drawable.LayerDrawable
import android.net.Uri
import android.util.AttributeSet
import android.util.Log
import androidx.appcompat.widget.AppCompatImageView
import com.skyjing.askit.R
import com.skyjing.askit.view.roundimageview.RoundedDrawable.Companion.fromBitmap
import com.skyjing.askit.view.roundimageview.RoundedDrawable.Companion.fromDrawable

class RoundedImageView : AppCompatImageView {
    var cornerRadius =
        DEFAULT_RADIUS
        private set
    var borderWidth =
        DEFAULT_BORDER_WIDTH
        private set
    var borderColors: ColorStateList? = ColorStateList
        .valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR)
        private set
    private var isOval = false
    private var mutateBackground = false
    private var mResource = 0
    private var mDrawable: Drawable? = null
    private var mBackgroundDrawable: Drawable? = null
    private var mScaleType: ScaleType? = null

    constructor(context: Context) : this(context , null , 0) {}

    @JvmOverloads
    constructor(
        context: Context,
        attrs: AttributeSet?,
        defStyle: Int = 0
    ) : super(context, attrs, defStyle) {
        val a = context.obtainStyledAttributes(
            attrs,
            R.styleable.RoundedImageView, defStyle, 0
        )
        val index = a
            .getInt(R.styleable.RoundedImageView_android_scaleType, -1)
        scaleType = if (index >= 0) {
            SCALE_TYPES[index]
        } else {
            // default scaletype to FIT_CENTER
            ScaleType.FIT_CENTER
        }
        cornerRadius = a.getDimensionPixelSize(
            R.styleable.RoundedImageView_corner_radius, -1
        ).toFloat()
        borderWidth = a.getDimensionPixelSize(
            R.styleable.RoundedImageView_border_width, -1
        ).toFloat()

        // don't allow negative values for radius and border
        if (cornerRadius < 0) {
            cornerRadius =
                DEFAULT_RADIUS
        }
        if (borderWidth < 0) {
            borderWidth =
                DEFAULT_BORDER_WIDTH
        }
        borderColors = a
            .getColorStateList(R.styleable.RoundedImageView_border_color)
        if (borderColors == null) {
            borderColors = ColorStateList
                .valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR)
        }
        mutateBackground = a.getBoolean(
            R.styleable.RoundedImageView_mutate_background, false
        )
        isOval = a.getBoolean(R.styleable.RoundedImageView_oval, false)
        updateDrawableAttrs()
        updateBackgroundDrawableAttrs(true)
        a.recycle()
    }

    override fun drawableStateChanged() {
        super.drawableStateChanged()
        invalidate()
    }

    /**
     * Return the current scale type in use by this ImageView.
     *
     * @attr ref android.R.styleable#ImageView_scaleType
     * @see ScaleType
     */
    override fun getScaleType(): ScaleType {
        return mScaleType!!
    }

    /**
     * Controls how the image should be resized or moved to match the size of
     * this ImageView.
     *
     * @param scaleType
     * The desired scaling mode.
     * @attr ref android.R.styleable#ImageView_scaleType
     */
    override fun setScaleType(scaleType: ScaleType) {
        assert(scaleType != null)
        if (mScaleType != scaleType) {
            mScaleType = scaleType
            when (scaleType) {
                ScaleType.CENTER, ScaleType.CENTER_CROP, ScaleType.CENTER_INSIDE, ScaleType.FIT_CENTER, ScaleType.FIT_START, ScaleType.FIT_END, ScaleType.FIT_XY -> super.setScaleType(
                    ScaleType.FIT_XY
                )
                else -> super.setScaleType(scaleType)
            }
            updateDrawableAttrs()
            updateBackgroundDrawableAttrs(false)
            invalidate()
        }
    }

    override fun setImageDrawable(drawable: Drawable?) {
        mResource = 0
        mDrawable = fromDrawable(drawable)
        updateDrawableAttrs()
        super.setImageDrawable(mDrawable)
    }

    override fun setImageBitmap(bm: Bitmap) {
        mResource = 0
        mDrawable = fromBitmap(bm)
        updateDrawableAttrs()
        super.setImageDrawable(mDrawable)
    }

    override fun setImageResource(resId: Int) {
        if (mResource != resId) {
            mResource = resId
            mDrawable = resolveResource()
            updateDrawableAttrs()
            super.setImageDrawable(mDrawable)
        }
    }

    override fun setImageURI(uri: Uri?) {
        super.setImageURI(uri)
        setImageDrawable(drawable)
    }

    private fun resolveResource(): Drawable? {
        val rsrc = resources ?: return null
        var d: Drawable? = null
        if (mResource != 0) {
            try {
                d = rsrc.getDrawable(mResource)
            } catch (e: Exception) {
                Log.e(
                    TAG,
                    "Unable to find resource: $mResource",
                    e
                )
                // Don't try again.
                mResource = 0
            }
        }
        return fromDrawable(d)
    }

    override fun setBackground(background: Drawable) {
        setBackgroundDrawable(background)
    }

    private fun updateDrawableAttrs() {
        updateAttrs(mDrawable)
    }

    private fun updateBackgroundDrawableAttrs(convert: Boolean) {
        if (mutateBackground) {
            if (convert) {
                mBackgroundDrawable = fromDrawable(mBackgroundDrawable)
            }
            updateAttrs(mBackgroundDrawable)
        }
    }

    private fun updateAttrs(drawable: Drawable?) {
        if (drawable == null) {
            return
        }
        if (drawable is RoundedDrawable) {
            drawable.setScaleType(
                mScaleType
            )
                .setCornerRadius(cornerRadius).setBorderWidth(borderWidth)
                .setBorderColor(borderColors).setOval(isOval)
        } else if (drawable is LayerDrawable) {
            // loop through layers to and set drawable attrs
            val ld = drawable
            var i = 0
            val layers = ld.numberOfLayers
            while (i < layers) {
                updateAttrs(ld.getDrawable(i))
                i++
            }
        }
    }

    @Deprecated("")
    override fun setBackgroundDrawable(background: Drawable) {
        mBackgroundDrawable = background
        updateBackgroundDrawableAttrs(true)
        super.setBackgroundDrawable(mBackgroundDrawable)
    }

    fun setCornerRadius(resId: Int) {
        setCornerRadius(resources.getDimension(resId))
    }

    fun setCornerRadius(radius: Float) {
        if (cornerRadius == radius) {
            return
        }
        cornerRadius = radius
        updateDrawableAttrs()
        updateBackgroundDrawableAttrs(false)
    }

    fun setBorderWidth(resId: Int) {
        setBorderWidth(resources.getDimension(resId))
    }

    fun setBorderWidth(width: Float) {
        if (borderWidth == width) {
            return
        }
        borderWidth = width
        updateDrawableAttrs()
        updateBackgroundDrawableAttrs(false)
        invalidate()
    }

    fun getBorderColor(): Int {
        return borderColors!!.defaultColor
    }

    fun setBorderColor(color: Int) {
        setBorderColor(ColorStateList.valueOf(color))
    }

    fun setBorderColor(colors: ColorStateList?) {
        if (borderColors == colors) {
            return
        }
        borderColors = colors ?: ColorStateList
            .valueOf(RoundedDrawable.DEFAULT_BORDER_COLOR)
        updateDrawableAttrs()
        updateBackgroundDrawableAttrs(false)
        if (borderWidth > 0) {
            invalidate()
        }
    }

    fun isOval(): Boolean {
        return isOval
    }

    fun setOval(oval: Boolean) {
        isOval = oval
        updateDrawableAttrs()
        updateBackgroundDrawableAttrs(false)
        invalidate()
    }

    fun isMutateBackground(): Boolean {
        return mutateBackground
    }

    fun setMutateBackground(mutate: Boolean) {
        if (mutateBackground == mutate) {
            return
        }
        mutateBackground = mutate
        updateBackgroundDrawableAttrs(true)
        invalidate()
    }

    companion object {
        const val TAG = "RoundedImageView"
        const val DEFAULT_RADIUS = 0f
        const val DEFAULT_BORDER_WIDTH = 0f
        private val SCALE_TYPES = arrayOf(
            ScaleType.MATRIX,
            ScaleType.FIT_XY, ScaleType.FIT_START, ScaleType.FIT_CENTER,
            ScaleType.FIT_END, ScaleType.CENTER, ScaleType.CENTER_CROP,
            ScaleType.CENTER_INSIDE
        )
    }
}